import camera from '@ohos.multimedia.camera';
import image from '@ohos.multimedia.image';
import common from '@ohos.app.ability.common';
import fs from '@ohos.file.fs';
import { getGlobalObject } from '../utils/GlobalThis';
import Logger from '../utils/Logger';
import { BusinessError } from '@ohos.base';
import securityLabel from '@ohos.file.securityLabel';
import promptAction from '@ohos.promptAction';

const NORMAL_PHOTO = camera.SceneMode.NORMAL_PHOTO // 普通拍照模式

const connectionTypeMap = new Map([
  [0, '本地相机'], // 内置相机
  [1, 'USB相机'], // USB连接的相机
  [2, '远程相机'] // 远程连接的相机
]);

const CameraPositionTypeMap = new Map([
  [0, '位置未指定'], // 相机位置未指定
  [1, '后置'], // 后置相机
  [2, '前置']// 前置相机
]);

const CameraTypeMap = new Map([
  [0, '类型未指定'], // 相机类型未指定
  [1, '广角相机'],
  [2, '超广角相机'],
  [3, '长焦相机'],
  [4, '景深相机'] // 带景深信息的相机
]);

const TAG = 'CameraService';

/**
 * 相机实现用到的API
 * @ohos.multimedia.camera (相机管理) 用于控制设备相机的主要 API。当您构建相机应用时，该 API 可用于获取相机设备列表、加载相机、加载预览输出模块、拍照和录像等功能。
 * @ohos.multimedia.image (图片处理) 用于接收拍照生成的图片数据。
 * XComponent 用于展示相机预览图像数据的组件。
 *
 * 相机实现步骤
 * 在配置文件中添加相机需要的权限，编写用户授权逻辑；permissions
 * 在页面中添加 XComponent 组件；
 * 初始化相机管理器，获取可用摄像头设备；Manager
 * 创建拍照会话实例；Session
 * 创建相机输入实例；CameraInput
 * 创建预览输出实例；PreviewOutput
 * 创建照片输出实例，并创建对应的保存逻辑；PhotoOutput
 * 配置拍照会话实例；Session.begin config Session.commit config
 * 开启拍照会话实例；Session.start
 * 在页面中添加拍照触发条件，并编写逻辑；
 *
 * 如何实现分布式？
 * 对于拍照：getSupportedCameras()获取远程摄像头并初始化
 * 对于保存：直接保存到分布式路径
 */


class CameraService {
  private static instance: CameraService;
  private cameraMgr: camera.CameraManager = undefined;
  private camerasArray: Array<camera.CameraDevice> = undefined;
  private cameraInput: camera.CameraInput = undefined;
  private previewOutput: camera.PreviewOutput = undefined;
  private photoOutPut: camera.PhotoOutput = undefined;
  private capSession: camera.PhotoSession = undefined;
  private capability: camera.CameraOutputCapability = undefined;
  private context: common.UIAbilityContext = undefined;
  private izDistributedCapture: boolean = false;

  public static getInstance(): CameraService {
    if (!CameraService.instance) {
      CameraService.instance = new CameraService();
    }
    return CameraService.instance;
  }

  private constructor() {
    this.context = getGlobalObject("context") as common.UIAbilityContext;
  }

  /**
   * 拍照回调注册类
   * @param photoOutput
   */
  async setPhotoOutputCb(photoOutput: camera.PhotoOutput) {
    // 设置回调之后，调用photoOutput的capture方法，就会将拍照的buffer回传到回调中
    // 分布式拍照无法进入该回调,新增distributedTakePicture
    photoOutput.on('photoAvailable', (errCode: BusinessError, photo: camera.Photo): void => {
      Logger.info(TAG, 'getPhoto start');
      Logger.info(TAG, `err: ${JSON.stringify(errCode)}`);
      if (errCode || photo === undefined) {
        Logger.error(TAG, 'getPhoto failed');
        return;
      }
      let imageObj: image.Image = photo.main;
      imageObj.getComponent(image.ComponentType.JPEG, (errCode: BusinessError, component: image.Component): Promise<void> => {
        Logger.info(TAG, 'getComponent start');
        if (errCode || component === undefined) {
          Logger.error(TAG, 'getComponent failed');
          return;
        }
        let buffer: ArrayBuffer;
        if (component.byteBuffer) {
          buffer = component.byteBuffer;
        } else {
          Logger.error(TAG, 'byteBuffer is null');
          return;
        }
        this.saveImage(buffer, imageObj);
      });
    });
    photoOutput.on('error', (err: BusinessError): void => {
      Logger.error(TAG, `${JSON.stringify(err)}`);
    });
  }

  /**
   * 远端拍照
   * @param surfaceId
   */
  distributedTakePicture(surfaceId: string) {
    // rk createPreviewOutput 只支持{"format":3,"size":{"width":640,"height":480}}
    let region: image.Region = { x: 0, y: 0, size: { width: 640, height: 480 } };
    image.createPixelMapFromSurface(surfaceId, region).then(async (pixelMap: image.PixelMap) => {
      Logger.info(TAG, 'Succeeded in creating pixelMap from Surface');

      let packOpts: image.PackingOption = { format: "image/jpeg", quality: 100 }
      const imagePackerApi: image.ImagePacker = image.createImagePacker();
      await pixelMap.rotate(-90.0);
      imagePackerApi.packing(pixelMap, packOpts, (err: BusinessError, data: ArrayBuffer) => {
        if (err) {
          Logger.error('Failed in packing the image.' + JSON.stringify(err));
        } else {
          this.saveImage(data);
          Logger.info(TAG, 'Succeeded in packing the image.');
          pixelMap.release();
        }
      })
    }).catch((error: BusinessError) => {
      Logger.error(TAG, 'Failed to create pixelMap ' + JSON.stringify(error));
    });
  }

  /**
   * 取消回调监听
   * @param photoOutput
   */
  clearPhotoOutputCb(photoOutput: camera.PhotoOutput) {
    photoOutput.off('photoAvailable', (errCode: BusinessError): void => {
      Logger.error(TAG, `off photoAvailable: ${JSON.stringify(errCode)}`);
    })
    photoOutput.off('error', (errCode: BusinessError): void => {
      Logger.error(TAG, `off error: ${JSON.stringify(errCode)}`);
    })
  }

  /**
   * 保存图片
   * @param buffer
   * @param img
   */
  saveImage(buffer: ArrayBuffer, img?: image.Image): void {
    Logger.info(TAG, 'savePicture begin');
    try {
      // 存到分布式相册
      let fileName = `${Date.now().toString()}.jpg`;
      let localPhotoPath = `${this.context.distributedFilesDir}/${fileName}`;
      let localFile: fs.File = fs.openSync(localPhotoPath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
      fs.writeSync(localFile.fd, buffer);
      fs.closeSync(localFile);
      Logger.info(TAG, `Succeeded in saving picture. ${localPhotoPath}`);

      // 设置文件的数据等级为s0
      securityLabel.setSecurityLabel(localPhotoPath, 's0').then(() => {
        Logger.info(TAG, `Succeeded in setSecurityLabeling. ${localPhotoPath}`);
      }).catch((err: BusinessError) => {
        Logger.error(TAG, `Failed to setSecurityLabel. Code: ${err.code}, message: ${err.message}`);
      });

      promptAction.showToast({
        message: `${fileName}保存成功`,
        duration: 2000,
      });
    } catch (e) {
      Logger.info(TAG, `savePicture error :${JSON.stringify(e.code)}`);
      Logger.info(TAG, `savePicture error :${JSON.stringify(e.message)}`);
    }
    img?.release();
  }

  /**
   * 初始化相机
   * @param surfaceId
   * @param cameraDevice
   * @returns
   */
  async initCamera(surfaceId: string, cameraDevice?: camera.CameraDevice): Promise<void> {
    Logger.info(TAG, `initCamera surfaceId:${surfaceId}`);
    if (cameraDevice) {
      // 释放资源
      await this.releaseCamera();
    }
    try {
      // 创建CameraManager对象
      if (!this.cameraMgr) {
        this.cameraMgr = camera.getCameraManager(this.context);
      }
    } catch (e) {
      Logger.info(TAG, `getCameraManager catch e:${JSON.stringify(e)}`);
      Logger.info(TAG, `getCameraManager catch code:${JSON.stringify(e.code)}`);
      Logger.info(TAG, `getCameraManager catch message:${JSON.stringify(e.message)}`);
    }
    Logger.info(TAG, 'getCameraManager end');
    Logger.info(TAG, `getCameraManager ${JSON.stringify(this.cameraMgr)}`);
    // 获取相机列表
    this.camerasArray = this.cameraMgr.getSupportedCameras();
    if (this.camerasArray != undefined && this.camerasArray.length > 0) {
      for (let index = 0; index < this.camerasArray.length; index++) {
        Logger.info(TAG, 'get cameras cameraId : ' + this.camerasArray[index].cameraId); // 获取相机ID
        Logger.info(TAG, 'get cameras cameraPosition : ' + this.camerasArray[index].cameraPosition); // 获取相机位置
        Logger.info(TAG, 'get cameras cameraType : ' + this.camerasArray[index].cameraType); // 获取相机类型
        Logger.info(TAG, 'get cameras connectionType : ' + this.camerasArray[index].connectionType); // 获取相机连接类型
      }
    }
    // 相机列表为空则返回
    if (this.camerasArray.length === 0) {
      Logger.info(TAG, 'cannot get cameras');
      return;
    }

    // 默认取本地摄像头，防止初始化时错误加载远端摄像头
    let mCamera = this.camerasArray.find((item) => item.connectionType === 0);

    if (cameraDevice) {
      // 处理入参
      mCamera = cameraDevice;
    }
    // 是否为远端摄像头拍照标志位
    this.izDistributedCapture = mCamera.connectionType === camera.ConnectionType.CAMERA_CONNECTION_REMOTE;

    // 获取相机支持的输出能力
    this.capability = this.cameraMgr.getSupportedOutputCapability(mCamera, NORMAL_PHOTO);
    Logger.info(TAG, `this.capability ${JSON.stringify(this.capability.previewProfiles)} `);

    let previewProfile = this.capability.previewProfiles[0];
    // 创建预览流
    this.previewOutput = this.cameraMgr.createPreviewOutput(
      previewProfile,
      surfaceId
    );
    Logger.info(TAG, `createPreviewOutput ${JSON.stringify(previewProfile)}`);

    let photoProfile = this.capability.photoProfiles[0];
    // 创建拍照流
    this.photoOutPut = this.cameraMgr.createPhotoOutput(
      photoProfile
    );
    Logger.info(TAG, `createPhotoOutput ${JSON.stringify(photoProfile)}`);

    Logger.info(TAG, 'createCameraInput');
    // 创建相机输入流
    this.cameraInput = this.cameraMgr.createCameraInput(mCamera);
    // 打开相机
    await this.cameraInput.open();

    // 创建会话
    this.capSession = this.cameraMgr.createSession(NORMAL_PHOTO);
    Logger.info(TAG, 'createCaptureSession');
    // 开始会话配置
    this.capSession.beginConfig();
    Logger.info(TAG, 'beginConfig');

    try {
      Logger.info(TAG, `captureSession addTest ${this.capSession.canAddInput(this.cameraInput)} ${this.capSession.canAddOutput(this.photoOutPut)} ${this.capSession.canAddOutput(this.previewOutput)}}`);
      // 向会话中添加相机输入流
      this.capSession.addInput(this.cameraInput);
      // 向会话中添加拍照输出流
      this.capSession.addOutput(this.photoOutPut);
      // 向会话中添加预览输出流
      this.capSession.addOutput(this.previewOutput);
      // 提交会话配置
      await this.capSession.commitConfig();
      // 启动会话
      await this.capSession.start();
    } catch (e) {
      Logger.error(TAG, 'captureSession addError' + JSON.stringify(e));
    }

    // 设置拍照回调
    this.setPhotoOutputCb(this.photoOutPut);

    Logger.info(TAG, 'captureSession start success.');
  }

  /**
   * 获取支持的相机数组
   * 须在初始化方法后调用
   * @returns
   */
  getSupportedCameras(): Array<camera.CameraDevice> {
    return this.cameraMgr.getSupportedCameras();
  }

  /**
   * 获取支持的相机数组字符串，用于展示
   * 须在初始化方法后调用
   * @returns
   */
  getSupportedCamerasString(): string[] {
    let result = [];
    let tmpCameras = this.getSupportedCameras();
    if (tmpCameras != undefined && tmpCameras.length > 0) {
      for (let index = 0; index < tmpCameras.length; index++) {
        result.push(`${connectionTypeMap.get(tmpCameras[index].connectionType)}`
          .concat(` ${CameraPositionTypeMap.get(tmpCameras[index].cameraPosition)}`)
          .concat(` ${CameraTypeMap.get(tmpCameras[index].cameraType)}`));
      }
    }
    return result;
  }

  /**
   * 拍照
   * @param surfaceId
   */
  takePicture(surfaceId: string): void {
    let photoSettings: camera.PhotoCaptureSetting = {
      rotation: camera.ImageRotation.ROTATION_0,
      quality: camera.QualityLevel.QUALITY_LEVEL_HIGH,
      location: {
        // 位置信息，经纬度
        latitude: 12.9698,
        longitude: 77.75,
        altitude: 1000,
      },
    };
    if (this.izDistributedCapture) {
      Logger.info(TAG, 'DistributedCapture takePicture');
      this.distributedTakePicture(surfaceId);
    } else {
      Logger.info(TAG, 'LocalCapture takePicture');
      this.photoOutPut.capture(photoSettings, async (err: BusinessError) => {
        if (err) {
          Logger.error(TAG, `Failed to capture the photo. error: ${JSON.stringify(err)}`);
          return;
        }
        Logger.info(TAG, 'Callback invoked to indicate the photo capture request success.');
      });
    }
    Logger.info(TAG, 'takePicture done');
  }

  /**
   * 资源释放
   */
  async releaseCamera(): Promise<void> {
    Logger.info(TAG, 'releaseCamera');
    if (this.cameraInput) {
      await this.cameraInput.close();
    }
    if (this.previewOutput) {
      await this.previewOutput.release();
    }
    if (this.photoOutPut) {
      this.clearPhotoOutputCb(this.photoOutPut);
      await this.photoOutPut.release();
    }
    if (this.capSession) {
      await this.capSession.release();
    }
  }
}

export default CameraService.getInstance();